package org.ns.summer.beans;

import org.ns.summer.beans.anns.Component;
import org.ns.summer.beans.anns.ComponentScanUtils;
import org.ns.summer.beans.anns.ComponentUtils;
import org.ns.summer.beans.anns.ProcessorOrderUtils;
import org.ns.summer.beans.beanprocessor.ApplicationContextAwareProcessor;
import org.ns.summer.beans.beanprocessor.AutowritedBeanProcessor;
import org.ns.summer.beans.beanprocessor.BeanFactoryAwareProcessor;
import org.ns.summer.beans.beanprocessor.ConfigurationClassPostProcessor;
import org.ns.summer.beans.definition.BeanDefinition;
import org.ns.summer.beans.definition.DefaultBeanDefinition;
import org.ns.summer.beans.error.BeansException;
import org.ns.summer.classpath.scan.ScanClassInfo;
import org.ns.summer.classpath.scan.ScanFile;
import org.ns.summer.classpath.scan.ScanFileExecute;
import org.ns.summer.classpath.scan.factory.DirAndJarScanFileFactory;
import org.ns.summer.core.AnnotationUtils;
import org.ns.summer.core.BeanUtils;
import org.ns.summer.core.ResourceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author 张星宇
 *
 * @email m.vip@qq.com
 *
 * 哈哈哈 极限 "减配" 版 IOC 容器管理
 */
public class AnnotationApplicationContext implements ApplicationContext {
    Logger log = LoggerFactory.getLogger(getClass());

    private List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();

    private ConfigurableBeanFactory beanFactory; // bean容器创建

    private Class<?> configBeanClass;
    private ClassLoader beanClassLoader;
    private String[] prefixs;
    public AnnotationApplicationContext(Class<?> configBeanClass) { // 配置最先加载的bean配置类
        this.configBeanClass = configBeanClass;
        this.beanClassLoader = configBeanClass.getClassLoader();
        // 加载启动配置的类
        doLoad(configBeanClass);
    }
    public AnnotationApplicationContext(Class<?> configBeanClass, String[] prefixs) { // 配置最先加载的bean配置类
        this.configBeanClass = configBeanClass;
        this.beanClassLoader = configBeanClass.getClassLoader();
        this.prefixs = prefixs;
        // 加载启动配置的类
        doLoad(configBeanClass);
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeansException {
        getBeanFactory().registerBeanDefinition(beanName, beanDefinition);
    }

    @Override
    public Object getBean(String name) throws BeansException {

        return getBeanFactory().getBean(name);
    }

    @Override
    public boolean containsBean(String name) {

        return getBeanFactory().containsBean(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(requiredType);
    }

    private void doLoad(Class<?> configBeanClass) {
        beanFactoryCreate();
        // 扫描classpath 下的所有文件和 所有jar中的文件并将@Component注解修饰的类载入
        scanConfigPath(configBeanClass);
        //
        prepareFacroryPost();
        // beanFacroryPostProcessor 创建
        beanFacroryPostProcessorCreate();
        // 初始化 bean工厂的配置
        prepareBeanFactory();
        // beanFacroryPostProcessor 执行
        invokeBeanFactoryPostProcessors();
        // 创建 beanPostProcessor 用于初始化bean操作
        beanPostProcessorCreate();
        // bean 真实创建过程
        beanCreate();
    }

    private void beanFactoryCreate() {
        beanFactory = new DefaultListableBeanFactory();
        beanFactory.setBeanClassLoader(beanClassLoader);
    }
    private void prepareFacroryPost() {
        this.beanFactoryPostProcessors.add(new ConfigurationClassPostProcessor());
    }
    private void beanFacroryPostProcessorCreate() {
        String[] beanNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class);
        for (String beanName : beanNames) {
            // 已经创建则不在进行创建
            if (beanFactory.containsSingleton(beanName)) {
                continue;
            }
            BeanFactoryPostProcessor beanPostProcessor = getBean(beanName, BeanFactoryPostProcessor.class); // 进行创建
            this.beanFactoryPostProcessors.add(beanPostProcessor);
        }

    }
    private void prepareBeanFactory() {
        beanFactory.addBeanPostProcessor(new BeanFactoryAwareProcessor());
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.addBeanPostProcessor(new ConfigurationClassPostProcessor()); // @ Bean
        beanFactory.addBeanPostProcessor(new AutowritedBeanProcessor()); // @ AutowriteBean
    }
    // 工厂执行
    private void invokeBeanFactoryPostProcessors() {


        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessors) {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        }


        //        beanFactory.addBeanPostProcessor(); 添加默认 BeanPostProcessor
    }

    private void beanPostProcessorCreate() {
        String[] beanNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class);

        List<BeanPostProcessor> nonBeanPostProcessors = new ArrayList<>();
        for (String beanName : beanNames) {
            BeanPostProcessor beanPostProcessor = beanPostProcessor = getBean(beanName, BeanPostProcessor.class);
            nonBeanPostProcessors.add(beanPostProcessor);
        }

        ProcessorOrderUtils.sort(nonBeanPostProcessors); // 排序
        for (BeanPostProcessor nonBeanPostProcessor : nonBeanPostProcessors) {
            beanFactory.addBeanPostProcessor(nonBeanPostProcessor);
        }
    }

    private void beanCreate() {
        getBeanFactory().preInstantiateSingletons();
    }

    /**
     *
     * 初始化 bean 并进行bean包装，例如AOP，设置注解字段注入
     * @param bean
     * @param beanName
     * @return 返回包装过的 bean
     */
    private Object initializeBean(Object bean, String beanName) {

        return beanFactory.initializeBean(bean, beanName);
    }

    private void scanConfigPath(Class<?> configBeanClass) {
        // 扫描的前缀
        String[] prefixs = ComponentScanUtils.getComponentPackagePath(configBeanClass, this.prefixs);
        String classpath = ResourceUtils.getClasspathRoot(configBeanClass);
        ScanFileExecute scanFileExecute = DirAndJarScanFileFactory.getScanFileExecute();
        scanFileExecute.addPrefixLimit(prefixs); // 设置前缀-扫描的时候通过前缀树快速筛选。
        List<ScanFile> scanFiles = scanFileExecute.execScan(classpath); // 开始扫描
        // 使用文件信息初始化
        for (ScanFile scanFile : scanFiles) {
            String className = scanFile.getClassName();
            if (className != null) { // class 文件
                registerBeanDefinition(scanFile);
            } else { // 其他文件，例如 *.xml, *.properties 等在这里载入

            }
        }
    }

    private void registerBeanDefinition(ScanFile scanFile) {
        try {
            ScanClassInfo scanClassInfo = scanFile.getScanClassInfo();
            // 不加载 接口和抽象类
            if (scanClassInfo.isInterfaceType() || scanClassInfo.isAbstractType()) {
                 //System.out.println(scanFile.getClassName());
                return;
            }

            Set<String> annotations = scanClassInfo.getAnnotations();
            ClassLoader classLoader = configBeanClass.getClassLoader();
            String beanClassName = scanFile.getClassName();
            boolean canRegisterBean = beanClassName.equals(this.configBeanClass.getName());

            if (!canRegisterBean) {
                for (String annotationClassName : annotations) {
                    Class<?> annType = BeanUtils.createClass(annotationClassName, classLoader);
                    // 不是 @Component 注解修饰的类不被加载
                    if (AnnotationUtils.existSuperAnnotation(annType, Component.class)) {
                        canRegisterBean = true;
                        break;
                    }
                }
            }
            if (!canRegisterBean) {
                return;
            }

            Component component = AnnotationUtils.getAnnotation(beanClassName, classLoader, Component.class);
            String beanName = ComponentUtils.getBeanName(beanClassName, component);
            DefaultBeanDefinition beanDefinition = new DefaultBeanDefinition();
            beanDefinition.setBeanClassName(beanClassName);
            beanDefinition.setClassLoader(classLoader);
            registerBeanDefinition(beanName, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("注册异常！", e);
        }
    }






    private Object newInstance(String className) {
        return null;
    }

    public ConfigurableBeanFactory getBeanFactory(){
        return this.beanFactory;
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }
}
